/*!
* @file    tick_timer.c
* @version v1.0
* @date    2018-02-05
* @author  苏勇 suyong_yq@126.com
* @brief   基于Systick的滴答定时器，实现功能包括：
*          - 基于硬件定时器的可靠延时
*          - 定时器超时回调(单次/周期)
*/

#include "tick_timer.h"
#include "fsl_device_registers.h"

volatile struct
{
    /* for blocking delay. */
    uint32_t BlockingDelayTicks;

    /* for Periodical delay with callback. */
    uint32_t PeriodicalIntervalTicks;
    uint32_t PeriodicalIntervalRemainTicks;
    void (*PeriodicalCallback)(void);

    /* for OneTime delay with callback. */
    uint32_t OneTimeRemainTicks;
    void (*OneTimeCallback)(void);
} gTickHandlerStruct;

extern uint32_t SystemCoreClock;

/* This API would config the SysTick and start it immediately. */
bool Tick_Init(uint32_t ticksPerSecond)
{
    if (0U == ticksPerSecond)
    {
        return false;
    }
    gTickHandlerStruct.PeriodicalIntervalTicks = 0U;
    gTickHandlerStruct.PeriodicalCallback = NULL;
    gTickHandlerStruct.OneTimeRemainTicks = 0U;
    gTickHandlerStruct.OneTimeCallback = NULL;
    gTickHandlerStruct.BlockingDelayTicks = 0U;

    SysTick_Config(SystemCoreClock/ticksPerSecond);

    return true;
}

void Tick_DelayBlocking(uint32_t ticks)
{
    gTickHandlerStruct.BlockingDelayTicks = ticks + 1U; /* At least 1 tick would be kept. */
    while (gTickHandlerStruct.BlockingDelayTicks > 0U)
    {
    }
}

void Tick_InstallOneTimeCallback(uint32_t ticks, void (*callback)(void))
{
    gTickHandlerStruct.OneTimeRemainTicks = ticks + 1U;
    gTickHandlerStruct.OneTimeCallback = callback;
}

void Tick_InstallPeriodicalCallback(uint32_t ticks, void (*callback)(void))
{
    gTickHandlerStruct.PeriodicalIntervalTicks = ticks;
    gTickHandlerStruct.PeriodicalIntervalRemainTicks = ticks;
    gTickHandlerStruct.PeriodicalCallback = callback;
}

/* Systick IRQ Handler. */
void SysTick_Handler(void)
{
    /* for blocking delay. */
    if (gTickHandlerStruct.BlockingDelayTicks > 0U)
    {
        gTickHandlerStruct.BlockingDelayTicks--;
    }

    /* for OneTime delay with callback. */
    if (gTickHandlerStruct.OneTimeRemainTicks > 0U)
    {
        gTickHandlerStruct.OneTimeRemainTicks--;
        if (   (0U == gTickHandlerStruct.OneTimeRemainTicks) 
            && (NULL != gTickHandlerStruct.OneTimeCallback) )
        {
            (*(gTickHandlerStruct.OneTimeCallback))();
            gTickHandlerStruct.OneTimeCallback = NULL;
        }
    }

    /* for Periodical delay with callback. */
    if (gTickHandlerStruct.PeriodicalIntervalRemainTicks > 0U)
    {
        gTickHandlerStruct.PeriodicalIntervalRemainTicks--;
        if (0U == gTickHandlerStruct.PeriodicalIntervalRemainTicks)
        {
            gTickHandlerStruct.PeriodicalIntervalRemainTicks = gTickHandlerStruct.PeriodicalIntervalTicks;
            if (NULL != gTickHandlerStruct.PeriodicalCallback)
            {
                (*(gTickHandlerStruct.PeriodicalCallback))();
            }
        }
    }

}

